home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / Crash.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  33.2 KB  |  1,269 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. // DO NOT USE stdio.h!  printf() calls malloc()!
  19. //#include <stdio.h>
  20. #include <stdarg.h>
  21. #include <crtdbg.h>
  22.  
  23. #include <windows.h>
  24. #include <tlhelp32.h>
  25.  
  26. #include "resource.h"
  27. #include "crash.h"
  28. #include "disasm.h"
  29. #include "oshelper.h"
  30. #include "helpfile.h"
  31. #include "list.h"
  32.  
  33. ///////////////////////////////////////////////////////////////////////////
  34.  
  35. #define CODE_WINDOW (256)
  36.  
  37. ///////////////////////////////////////////////////////////////////////////
  38.  
  39. extern HINSTANCE g_hInst;
  40. extern "C" unsigned long version_num;
  41.  
  42. static CodeDisassemblyWindow *g_pcdw;
  43.  
  44. ///////////////////////////////////////////////////////////////////////////
  45.  
  46. BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
  47.  
  48. ///////////////////////////////////////////////////////////////////////////
  49.  
  50. #ifdef _DEBUG
  51.  
  52. void checkfpustack(const char *file, const int line) throw() {
  53.     static const char szFPUProblemCaption[]="FPU/MMX internal problem";
  54.     static const char szFPUProblemMessage[]="The FPU stack wasn't empty!  Tagword = %04x\nFile: %s, line %d";
  55.     static bool seenmsg=false;
  56.  
  57.     char    buf[128];
  58.     unsigned short tagword;
  59.  
  60.     if (seenmsg)
  61.         return;
  62.  
  63.     __asm fnstenv buf
  64.  
  65.     tagword = *(unsigned short *)(buf + 8);
  66.  
  67.     if (tagword != 0xffff) {
  68.         wsprintf(buf, szFPUProblemMessage, tagword, file, line);
  69.         MessageBox(NULL, buf, szFPUProblemCaption, MB_OK);
  70.         seenmsg=true;
  71.     }
  72.  
  73. }
  74.  
  75. void __declspec(naked) *operator new(size_t bytes) {
  76.     static const char fname[]="stack trace";
  77.  
  78.     __asm {
  79.         push    ebp
  80.         mov        ebp,esp
  81.  
  82.         push    [ebp+4]                ;return address
  83.         push    offset fname        ;'filename'
  84.         push    _NORMAL_BLOCK        ;block type
  85.         push    [ebp+8]                ;allocation size
  86.  
  87.         call    _malloc_dbg
  88.         add        esp,16
  89.  
  90.         pop        ebp
  91.         ret
  92.     }
  93. }
  94.  
  95. #endif
  96.  
  97. #if 0
  98. void __declspec(naked) stackcheck(void *&sp) {
  99.     static const char g_szStackHemorrhage[]="WARNING: Thread is hemorrhaging stack space!\n";
  100.  
  101.     __asm {
  102.         mov        eax,[esp+4]
  103.         mov        ecx,[eax]
  104.         or        ecx,ecx
  105.         jnz        started
  106.         mov        [eax],esp
  107.         ret
  108. started:
  109.         sub        ecx,esp
  110.         mov        eax,ecx
  111.         sar        ecx,31
  112.         xor        eax,ecx
  113.         sub        eax,ecx
  114.         cmp        eax,128
  115.         jb        ok
  116.         push    offset g_szStackHemorrhage
  117.         call    dword ptr [OutputDebugString]
  118.         int        3
  119. ok:
  120.         ret
  121.     }
  122. }
  123. #endif
  124.  
  125. ///////////////////////////////////////////////////////////////////////////
  126. //
  127. //    Nina's per-thread debug logs are really handy, so I back-ported
  128. //    them to 1.x.  These are lightweight in comparison, however.
  129. //
  130.  
  131. VirtualDubThreadState __declspec(thread) g_PerThreadState;
  132. __declspec(thread)
  133. struct {
  134.     ListNode node;
  135.     VirtualDubThreadState *pState;
  136. } g_PerThreadStateNode;
  137.  
  138. class VirtualDubThreadStateNode : public ListNode2<VirtualDubThreadStateNode> {
  139. public:
  140.     VirtualDubThreadState *pState;
  141. };
  142.  
  143. static CRITICAL_SECTION g_csPerThreadState;
  144. static List2<VirtualDubThreadStateNode> g_listPerThreadState;
  145. static LONG g_nThreadsTrackedMinusOne = -1;
  146.  
  147. void VirtualDubInitializeThread(const char *pszName) {
  148.     DWORD dwThreadId = GetCurrentThreadId();
  149.  
  150.     if (!InterlockedIncrement(&g_nThreadsTrackedMinusOne)) {
  151.         InitializeCriticalSection(&g_csPerThreadState);
  152.     }
  153.  
  154.     EnterCriticalSection(&g_csPerThreadState);
  155.  
  156.     if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), (HANDLE *)&g_PerThreadState.hThread, NULL, FALSE, DUPLICATE_SAME_ACCESS)) {
  157.  
  158.         g_PerThreadState.pszThreadName = pszName;
  159.         g_PerThreadState.dwThreadId = dwThreadId;
  160.  
  161.         g_PerThreadStateNode.pState = &g_PerThreadState;
  162.         g_listPerThreadState.AddTail((ListNode2<VirtualDubThreadStateNode> *)&g_PerThreadStateNode);
  163.     }
  164.  
  165.     LeaveCriticalSection(&g_csPerThreadState);
  166. }
  167.  
  168. void VirtualDubDeinitializeThread() {
  169.     EnterCriticalSection(&g_csPerThreadState);
  170.  
  171.     ((ListNode2<VirtualDubThreadStateNode> *)&g_PerThreadStateNode)->Remove();
  172.  
  173.     LeaveCriticalSection(&g_csPerThreadState);
  174.  
  175.     if (g_PerThreadState.pszThreadName)
  176.         CloseHandle((HANDLE)g_PerThreadState.hThread);
  177.  
  178.     InterlockedDecrement(&g_nThreadsTrackedMinusOne);
  179. }
  180.  
  181. ///////////////////////////////////////////////////////////////////////////
  182.  
  183. static const struct ExceptionLookup {
  184.     DWORD    code;
  185.     const char *name;
  186. } exceptions[]={
  187.     {    EXCEPTION_ACCESS_VIOLATION,            "Access Violation"        },
  188.     {    EXCEPTION_BREAKPOINT,                "Breakpoint"            },
  189.     {    EXCEPTION_FLT_DENORMAL_OPERAND,        "FP Denormal Operand"    },
  190.     {    EXCEPTION_FLT_DIVIDE_BY_ZERO,        "FP Divide-by-Zero"        },
  191.     {    EXCEPTION_FLT_INEXACT_RESULT,        "FP Inexact Result"        },
  192.     {    EXCEPTION_FLT_INVALID_OPERATION,    "FP Invalid Operation"    },
  193.     {    EXCEPTION_FLT_OVERFLOW,                "FP Overflow",            },
  194.     {    EXCEPTION_FLT_STACK_CHECK,            "FP Stack Check",        },
  195.     {    EXCEPTION_FLT_UNDERFLOW,            "FP Underflow",            },
  196.     {    EXCEPTION_INT_DIVIDE_BY_ZERO,        "Integer Divide-by-Zero",    },
  197.     {    EXCEPTION_INT_OVERFLOW,                "Integer Overflow",        },
  198.     {    EXCEPTION_PRIV_INSTRUCTION,            "Privileged Instruction",    },
  199.     {    EXCEPTION_ILLEGAL_INSTRUCTION,        "Illegal instruction"    },
  200.     {    0xe06d7363,                            "Unhandled Microsoft C++ Exception",    },
  201.             // hmm... '_msc'... gee, who would have thought?
  202.     {    NULL    },
  203. };
  204.  
  205. LONG __stdcall CrashHandler(EXCEPTION_POINTERS *pExc) {
  206.     SetUnhandledExceptionFilter(NULL);
  207.  
  208.     /////////////////////////
  209.     //
  210.     // QUICKLY: SUSPEND ALL THREADS THAT AREN'T US.
  211.  
  212.     EnterCriticalSection(&g_csPerThreadState);
  213.  
  214.     try {
  215.         DWORD dwCurrentThread = GetCurrentThreadId();
  216.  
  217.         for(List2<VirtualDubThreadStateNode>::fwit it = g_listPerThreadState.begin(); it; ++it) {
  218.             const VirtualDubThreadState *pState = it->pState;
  219.  
  220.             if (pState->dwThreadId && pState->dwThreadId != dwCurrentThread) {
  221.                 SuspendThread((HANDLE)pState->hThread);
  222.             }
  223.         }
  224.     } catch(...) {
  225.     }
  226.  
  227.     LeaveCriticalSection(&g_csPerThreadState);
  228.  
  229.     /////////////////////////
  230.  
  231.     static char buf[CODE_WINDOW+16];
  232.     HANDLE hprMe = GetCurrentProcess();
  233.     void *lpBaseAddress = pExc->ExceptionRecord->ExceptionAddress;
  234.     char *lpAddr = (char *)((long)lpBaseAddress & -32);
  235.  
  236.     memset(buf, 0, sizeof buf);
  237.  
  238.     if ((unsigned long)lpAddr > CODE_WINDOW/2)
  239.         lpAddr -= CODE_WINDOW/2;
  240.     else
  241.         lpAddr = NULL;
  242.  
  243.     if (!ReadProcessMemory(hprMe, lpAddr, buf, CODE_WINDOW, NULL)) {
  244.         int i;
  245.  
  246.         for(i=0; i<CODE_WINDOW; i+=32)
  247.             if (!ReadProcessMemory(hprMe, lpAddr+i, buf+i, 32, NULL))
  248.                 memset(buf+i, 0, 32);
  249.     }
  250.  
  251.     CodeDisassemblyWindow cdw(buf, CODE_WINDOW, (char *)(buf-lpAddr), lpAddr);
  252.  
  253.     g_pcdw = &cdw;
  254.  
  255.     cdw.setFaultAddress(lpBaseAddress);
  256.  
  257.     DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_DISASM_CRASH), NULL, CrashDlgProc, (LPARAM)pExc);
  258.  
  259.     UnhandledExceptionFilter(pExc);
  260.  
  261.     return EXCEPTION_EXECUTE_HANDLER;
  262. }
  263.  
  264. static void Report(HWND hwndList, HANDLE hFile, const char *format, ...) {
  265.     char buf[256];
  266.     va_list val;
  267.     int ch;
  268.  
  269.     va_start(val, format);
  270.     ch = wvsprintf(buf, format, val);
  271.     va_end(val);
  272.  
  273.     if (hwndList)
  274.         SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)buf);
  275.  
  276.     if (hFile) {
  277.         DWORD dwActual;
  278.  
  279.         buf[ch] = '\r';
  280.         buf[ch+1] = '\n';
  281.         WriteFile(hFile, buf, ch+2, &dwActual, NULL);
  282.         FlushFileBuffers(hFile);
  283.     }
  284. }
  285.  
  286. static void SetWindowTitlef(HWND hwnd, const char *format, ...) {
  287.     char buf[256];
  288.     va_list val;
  289.  
  290.     va_start(val, format);
  291.     wvsprintf(buf, format, val);
  292.     va_end(val);
  293.     SetWindowText(hwnd, buf);
  294. }
  295.  
  296. static void ReportCrashData(HWND hwnd, HWND hwndReason, HANDLE hFile, const EXCEPTION_POINTERS *const pExc) {
  297.     const EXCEPTION_RECORD *const pRecord = (const EXCEPTION_RECORD *)pExc->ExceptionRecord;
  298.     const CONTEXT *const pContext = (const CONTEXT *)pExc->ContextRecord;
  299.     int i, tos;
  300.  
  301.     Report(hwnd, hFile, "EAX = %08lx", pContext->Eax);
  302.     Report(hwnd, hFile, "EBX = %08lx", pContext->Ebx);
  303.     Report(hwnd, hFile, "ECX = %08lx", pContext->Ecx);
  304.     Report(hwnd, hFile, "EDX = %08lx", pContext->Edx);
  305.     Report(hwnd, hFile, "EBP = %08lx", pContext->Ebp);
  306.     Report(hwnd, hFile, "DS:ESI = %04x:%08lx", pContext->SegDs, pContext->Esi);
  307.     Report(hwnd, hFile, "ES:EDI = %04x:%08lx", pContext->SegEs, pContext->Edi);
  308.     Report(hwnd, hFile, "SS:ESP = %04x:%08lx", pContext->SegSs, pContext->Esp);
  309.     Report(hwnd, hFile, "CS:EIP = %04x:%08lx", pContext->SegCs, pContext->Eip);
  310.     Report(hwnd, hFile, "FS = %04x", pContext->SegFs);
  311.     Report(hwnd, hFile, "GS = %04x", pContext->SegGs);
  312.     Report(hwnd, hFile, "EFLAGS = %08lx", pContext->EFlags);
  313.     Report(hwnd, hFile, "");
  314.  
  315.     // extract out MMX registers
  316.  
  317.     tos = (pContext->FloatSave.StatusWord & 0x3800)>>11;
  318.  
  319.     for(i=0; i<8; i++) {
  320.         long *pReg = (long *)(pContext->FloatSave.RegisterArea + 10*((i-tos) & 7));
  321.  
  322.         Report(hwnd, hFile, "MM%c = %08lx%08lx", i+'0', pReg[0], pReg[1]);
  323.     }
  324.  
  325.     // fill out bomb reason
  326.  
  327.     const struct ExceptionLookup *pel = exceptions;
  328.  
  329.     while(pel->code) {
  330.         if (pel->code == pRecord->ExceptionCode)
  331.             break;
  332.  
  333.         ++pel;
  334.     }
  335.  
  336.     // Unfortunately, EXCEPTION_ACCESS_VIOLATION doesn't seem to provide
  337.     // us with the read/write flag and virtual address as the docs say...
  338.     // *sigh*
  339.  
  340.     if (!pel->code) {
  341.         if (hwndReason)
  342.             SetWindowTitlef(hwndReason, "Crash reason: unknown exception 0x%08lx", pRecord->ExceptionCode);
  343.  
  344.         if (hFile)
  345.             Report(NULL, hFile, "Crash reason: unknown exception 0x%08lx", pRecord->ExceptionCode);
  346.     } else {
  347.         if (hwndReason)
  348.             SetWindowTitlef(hwndReason, "Crash reason: %s", pel->name);
  349.  
  350.         if (hFile)
  351.             Report(NULL, hFile, "Crash reason: %s", pel->name);
  352.     }
  353.  
  354.     // Dump thread stacks
  355.  
  356.     Report(NULL, hFile, "");
  357.  
  358.     EnterCriticalSection(&g_csPerThreadState);
  359.  
  360.     try {
  361.         for(List2<VirtualDubThreadStateNode>::fwit it = g_listPerThreadState.begin(); it; ++it) {
  362.             const VirtualDubThreadState *pState = it->pState;
  363.  
  364.             Report(NULL, hFile, "Thread %08lx (%s)", pState->dwThreadId, pState->pszThreadName?pState->pszThreadName:"unknown");
  365.  
  366.             for(int i=0; i<CHECKPOINT_COUNT; ++i) {
  367.                 const VirtualDubCheckpoint& cp = pState->cp[(pState->nNextCP+i) & (CHECKPOINT_COUNT-1)];
  368.  
  369.                 if (cp.file)
  370.                     Report(NULL, hFile, "\t%s(%d)", cp.file, cp.line);
  371.             }
  372.         }
  373.     } catch(...) {
  374.     }
  375.  
  376.     LeaveCriticalSection(&g_csPerThreadState);
  377.  
  378.     Report(NULL, hFile, "");
  379. }
  380.  
  381. static const char *GetNameFromHeap(const char *heap, int idx) {
  382.     while(idx--)
  383.         while(*heap++);
  384.  
  385.     return heap;
  386. }
  387.  
  388. static void SpliceProgramPath(char *buf, int bufsiz, const char *fn) {
  389.     char tbuf[MAX_PATH];
  390.     char *pszFile;
  391.  
  392.     GetModuleFileName(NULL, tbuf, sizeof tbuf);
  393.     GetFullPathName(tbuf, bufsiz, buf, &pszFile);
  394.     strcpy(pszFile, fn);
  395. }
  396.  
  397. //////////////////////////////////////////////////////////////////////////////
  398.  
  399. static bool IsValidCall(char *buf, int len) {
  400.     // Permissible CALL sequences that we care about:
  401.     //
  402.     //    E8 xx xx xx xx            CALL near relative
  403.     //    FF (group 2)            CALL near absolute indirect
  404.     //
  405.     // Minimum sequence is 2 bytes (call eax).
  406.     // Maximum sequence is 7 bytes (call dword ptr [eax+disp32]).
  407.  
  408.     if (len >= 5 && buf[-5] == (char)0xE8)
  409.         return true;
  410.  
  411.     // FF 14 xx                    CALL [reg32+reg32*scale]
  412.  
  413.     if (len >= 3 && buf[-3] == (char)0xFF && buf[-2]==0x14)
  414.         return true;
  415.  
  416.     // FF 15 xx xx xx xx        CALL disp32
  417.  
  418.     if (len >= 6 && buf[-6] == (char)0xFF && buf[-5]==0x15)
  419.         return true;
  420.  
  421.     // FF 00-3F(!14/15)            CALL [reg32]
  422.  
  423.     if (len >= 2 && buf[-2] == (char)0xFF && (unsigned char)buf[-1] < 0x40)
  424.         return true;
  425.  
  426.     // FF D0-D7                    CALL reg32
  427.  
  428.     if (len >= 2 && buf[-2] == (char)0xFF && (buf[-1]&0xF8) == 0xD0)
  429.         return true;
  430.  
  431.     // FF 50-57 xx                CALL [reg32+reg32*scale+disp8]
  432.  
  433.     if (len >= 3 && buf[-3] == (char)0xFF && (buf[-2]&0xF8) == 0x50)
  434.         return true;
  435.  
  436.     // FF 90-97 xx xx xx xx xx    CALL [reg32+reg32*scale+disp32]
  437.  
  438.     if (len >= 7 && buf[-7] == (char)0xFF && (buf[-6]&0xF8) == 0x90)
  439.         return true;
  440.  
  441.     return false;
  442. }
  443.  
  444. //////////////////////////////////////////////////////////////////////////////
  445.  
  446. struct ModuleInfo {
  447.     const char *name;
  448.     unsigned long base, size;
  449. };
  450.  
  451. // ARRGH.  Where's psapi.h?!?
  452.  
  453. struct Win32ModuleInfo {
  454.     DWORD base, size, entry;
  455. };
  456.  
  457. typedef BOOL (__stdcall *PENUMPROCESSMODULES)(HANDLE, HMODULE *, DWORD, LPDWORD);
  458. typedef DWORD (__stdcall *PGETMODULEBASENAME)(HANDLE, HMODULE, LPTSTR, DWORD);
  459. typedef BOOL (__stdcall *PGETMODULEINFORMATION)(HANDLE, HMODULE, Win32ModuleInfo *, DWORD);
  460.  
  461. typedef HANDLE (__stdcall *PCREATETOOLHELP32SNAPSHOT)(DWORD, DWORD);
  462. typedef BOOL (WINAPI *PMODULE32FIRST)(HANDLE, LPMODULEENTRY32);
  463. typedef BOOL (WINAPI *PMODULE32NEXT)(HANDLE, LPMODULEENTRY32);
  464.  
  465. static ModuleInfo *CrashGetModules(void *&ptr) {
  466.     void *pMem = VirtualAlloc(NULL, 65536, MEM_COMMIT, PAGE_READWRITE);
  467.     char szName[MAX_PATH];
  468.  
  469.     if (!pMem) {
  470.         ptr = NULL;
  471.         return NULL;
  472.     }
  473.  
  474.     // This sucks.  If we're running under Windows 9x, we must use
  475.     // TOOLHELP.DLL to get the module list.  Under Windows NT, we must
  476.     // use PSAPI.DLL.  With Windows 2000, we can use both (but prefer
  477.     // PSAPI.DLL).
  478.  
  479.     HMODULE hmodPSAPI = LoadLibrary("psapi.dll");
  480.  
  481.     if (hmodPSAPI) {
  482.         // Using PSAPI.DLL.  Call EnumProcessModules(), then GetModuleFileNameEx()
  483.         // and GetModuleInformation().
  484.  
  485.         PENUMPROCESSMODULES pEnumProcessModules = (PENUMPROCESSMODULES)GetProcAddress(hmodPSAPI, "EnumProcessModules");
  486.         PGETMODULEBASENAME pGetModuleBaseName = (PGETMODULEBASENAME)GetProcAddress(hmodPSAPI, "GetModuleBaseNameA");
  487.         PGETMODULEINFORMATION pGetModuleInformation = (PGETMODULEINFORMATION)GetProcAddress(hmodPSAPI, "GetModuleInformation");
  488.         HMODULE *pModules, *pModules0 = (HMODULE *)((char *)pMem + 0xF000);
  489.         DWORD cbNeeded;
  490.  
  491.         if (pEnumProcessModules && pGetModuleBaseName && pGetModuleInformation
  492.             && pEnumProcessModules(GetCurrentProcess(), pModules0, 0x1000, &cbNeeded)) {
  493.  
  494.             ModuleInfo *pMod, *pMod0;
  495.             char *pszHeap = (char *)pMem, *pszHeapLimit;
  496.  
  497.             if (cbNeeded > 0x1000) cbNeeded = 0x1000;
  498.  
  499.             pModules = (HMODULE *)((char *)pMem + 0x10000 - cbNeeded);
  500.             memmove(pModules, pModules0, cbNeeded);
  501.  
  502.             pMod = pMod0 = (ModuleInfo *)((char *)pMem + 0x10000 - sizeof(ModuleInfo) * (cbNeeded / sizeof(HMODULE) + 1));
  503.             pszHeapLimit = (char *)pMod;
  504.  
  505.             do {
  506.                 HMODULE hCurMod = *pModules++;
  507.                 Win32ModuleInfo mi;
  508.  
  509.                 if (pGetModuleBaseName(GetCurrentProcess(), hCurMod, pszHeap, pszHeapLimit - pszHeap)
  510.                     && pGetModuleInformation(GetCurrentProcess(), hCurMod, &mi, sizeof mi)) {
  511.  
  512.                     char *period = NULL;
  513.  
  514.                     pMod->name = pszHeap;
  515.  
  516.                     while(*pszHeap++)
  517.                         if (pszHeap[-1] == '.')
  518.                             period = pszHeap-1;
  519.  
  520.                     if (period) {
  521.                         *period = 0;
  522.                         pszHeap = period+1;
  523.                     }
  524.  
  525.                     pMod->base = mi.base;
  526.                     pMod->size = mi.size;
  527.                     ++pMod;
  528.                 }
  529.             } while((cbNeeded -= sizeof(HMODULE *)) > 0);
  530.  
  531.             pMod->name = NULL;
  532.  
  533.             FreeLibrary(hmodPSAPI);
  534.             ptr = pMem;
  535.             return pMod0;
  536.         }
  537.  
  538.         FreeLibrary(hmodPSAPI);
  539.     } else {
  540.         // No PSAPI.  Use the ToolHelp functions in KERNEL.
  541.  
  542.         HMODULE hmodKERNEL32 = LoadLibrary("kernel32.dll");
  543.  
  544.         PCREATETOOLHELP32SNAPSHOT pCreateToolhelp32Snapshot = (PCREATETOOLHELP32SNAPSHOT)GetProcAddress(hmodKERNEL32, "CreateToolhelp32Snapshot");
  545.         PMODULE32FIRST pModule32First = (PMODULE32FIRST)GetProcAddress(hmodKERNEL32, "Module32First");
  546.         PMODULE32NEXT pModule32Next = (PMODULE32NEXT)GetProcAddress(hmodKERNEL32, "Module32Next");
  547.         HANDLE hSnap;
  548.  
  549.         if (pCreateToolhelp32Snapshot && pModule32First && pModule32Next) {
  550.             if ((HANDLE)-1 != (hSnap = pCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0))) {
  551.                 ModuleInfo *pModInfo = (ModuleInfo *)((char *)pMem + 65536);
  552.                 char *pszHeap = (char *)pMem;
  553.                 MODULEENTRY32 me;
  554.  
  555.                 --pModInfo;
  556.                 pModInfo->name = NULL;
  557.  
  558.                 me.dwSize = sizeof(MODULEENTRY32);
  559.  
  560.                 if (pModule32First(hSnap, &me))
  561.                     do {
  562.                         if (pszHeap+strlen(me.szModule) >= (char *)(pModInfo - 1))
  563.                             break;
  564.  
  565.                         strcpy(pszHeap, me.szModule);
  566.  
  567.                         --pModInfo;
  568.                         pModInfo->name = pszHeap;
  569.  
  570.                         char *period = NULL;
  571.  
  572.                         while(*pszHeap++);
  573.                             if (pszHeap[-1]=='.')
  574.                                 period = pszHeap-1;
  575.  
  576.                         if (period) {
  577.                             *period = 0;
  578.                             pszHeap = period+1;
  579.                         }
  580.  
  581.                         pModInfo->base = (unsigned long)me.modBaseAddr;
  582.                         pModInfo->size = me.modBaseSize;
  583.  
  584.                     } while(pModule32Next(hSnap, &me));
  585.  
  586.                 CloseHandle(hSnap);
  587.  
  588.                 FreeLibrary(hmodKERNEL32);
  589.  
  590.                 ptr = pMem;
  591.                 return pModInfo;
  592.             }
  593.         }
  594.  
  595.         FreeLibrary(hmodKERNEL32);
  596.     }
  597.  
  598.     VirtualFree(pMem, 0, MEM_RELEASE);
  599.  
  600.     ptr = NULL;
  601.     return NULL;
  602. }
  603.  
  604. ///////////////////////////////////////////////////////////////////////////
  605. //
  606. //    info from Portable Executable/Common Object File Format (PE/COFF) spec
  607.  
  608. typedef unsigned short ushort;
  609. typedef unsigned long ulong;
  610.  
  611. struct PEHeader {
  612.     ulong        signature;
  613.     ushort        machine;
  614.     ushort        sections;
  615.     ulong        timestamp;
  616.     ulong        symbol_table;
  617.     ulong        symbols;
  618.     ushort        opthdr_size;
  619.     ushort        characteristics;
  620. };
  621.  
  622. struct PESectionHeader {
  623.     char        name[8];
  624.     ulong        virtsize;
  625.     ulong        virtaddr;
  626.     ulong        rawsize;
  627.     ulong        rawptr;
  628.     ulong        relocptr;
  629.     ulong        linenoptr;
  630.     ushort        reloc_cnt;
  631.     ushort        lineno_cnt;
  632.     ulong        characteristics;
  633. };
  634.  
  635. struct PEExportDirectory {
  636.     ulong        flags;
  637.     ulong        timestamp;
  638.     ushort        major;
  639.     ushort        minor;
  640.     ulong        nameptr;
  641.     ulong        ordbase;
  642.     ulong        addrtbl_cnt;
  643.     ulong        nametbl_cnt;
  644.     ulong        addrtbl_ptr;
  645.     ulong        nametbl_ptr;
  646.     ulong        ordtbl_ptr;
  647. };
  648.  
  649. struct PE32OptionalHeader {
  650.     ushort        magic;                    // 0
  651.     char        major_linker_ver;        // 2
  652.     char        minor_linker_ver;        // 3
  653.     ulong        codesize;                // 4
  654.     ulong        idatasize;                // 8
  655.     ulong        udatasize;                // 12
  656.     ulong        entrypoint;                // 16
  657.     ulong        codebase;                // 20
  658.     ulong        database;                // 24
  659.     ulong        imagebase;                // 28
  660.     ulong        section_align;            // 32
  661.     ulong        file_align;                // 36
  662.     ushort        majoros;                // 40
  663.     ushort        minoros;                // 42
  664.     ushort        majorimage;                // 44
  665.     ushort        minorimage;                // 46
  666.     ushort        majorsubsys;            // 48
  667.     ushort        minorsubsys;            // 50
  668.     ulong        reserved;                // 52
  669.     ulong        imagesize;                // 56
  670.     ulong        hdrsize;                // 60
  671.     ulong        checksum;                // 64
  672.     ushort        subsystem;                // 68
  673.     ushort        characteristics;        // 70
  674.     ulong        stackreserve;            // 72
  675.     ulong        stackcommit;            // 76
  676.     ulong        heapreserve;            // 80
  677.     ulong        heapcommit;                // 84
  678.     ulong        loaderflags;            // 88
  679.     ulong        dictentries;            // 92
  680.  
  681.     // Not part of header, but it's convienent here
  682.  
  683.     ulong        export_RVA;                // 96
  684.     ulong        export_size;            // 100
  685. };
  686.  
  687. struct PE32PlusOptionalHeader {
  688.     ushort        magic;                    // 0
  689.     char        major_linker_ver;        // 2
  690.     char        minor_linker_ver;        // 3
  691.     ulong        codesize;                // 4
  692.     ulong        idatasize;                // 8
  693.     ulong        udatasize;                // 12
  694.     ulong        entrypoint;                // 16
  695.     ulong        codebase;                // 20
  696.     __int64        imagebase;                // 24
  697.     ulong        section_align;            // 32
  698.     ulong        file_align;                // 36
  699.     ushort        majoros;                // 40
  700.     ushort        minoros;                // 42
  701.     ushort        majorimage;                // 44
  702.     ushort        minorimage;                // 46
  703.     ushort        majorsubsys;            // 48
  704.     ushort        minorsubsys;            // 50
  705.     ulong        reserved;                // 52
  706.     ulong        imagesize;                // 56
  707.     ulong        hdrsize;                // 60
  708.     ulong        checksum;                // 64
  709.     ushort        subsystem;                // 68
  710.     ushort        characteristics;        // 70
  711.     __int64        stackreserve;            // 72
  712.     __int64        stackcommit;            // 80
  713.     __int64        heapreserve;            // 88
  714.     __int64        heapcommit;                // 96
  715.     ulong        loaderflags;            // 104
  716.     ulong        dictentries;            // 108
  717.  
  718.     // Not part of header, but it's convienent here
  719.  
  720.     ulong        export_RVA;                // 112
  721.     ulong        export_size;            // 116
  722. };
  723.  
  724. static const char *CrashLookupExport(HMODULE hmod, unsigned long addr, unsigned long &fnbase) {
  725.     char *pBase = (char *)hmod;
  726.  
  727.     // The PEheader offset is at hmod+0x3c.  Add the size of the optional header
  728.     // to step to the section headers.
  729.  
  730.     PEHeader *pHeader = (PEHeader *)(pBase + ((long *)hmod)[15]);
  731.  
  732.     if (pHeader->signature != 'EP')
  733.         return NULL;
  734.  
  735. #if 0
  736.     PESectionHeader *pSHdrs = (PESectionHeader *)((char *)pHeader + sizeof(PEHeader) + pHeader->opthdr_size);
  737.  
  738.     // Scan down the section headers and look for ".edata"
  739.  
  740.     int i;
  741.  
  742.     for(i=0; i<pHeader->sections; i++) {
  743.         MessageBox(NULL, pSHdrs[i].name, "section", MB_OK);
  744.         if (!memcmp(pSHdrs[i].name, ".edata", 6))
  745.             break;
  746.     }
  747.  
  748.     if (i >= pHeader->sections)
  749.         return NULL;
  750. #endif
  751.  
  752.     // Verify the optional structure.
  753.  
  754.     PEExportDirectory *pExportDir;
  755.  
  756.     if (pHeader->opthdr_size < 104)
  757.         return NULL;
  758.  
  759.     switch(*(short *)((char *)pHeader + sizeof(PEHeader))) {
  760.     case 0x10b:        // PE32
  761.         {
  762.             PE32OptionalHeader *pOpt = (PE32OptionalHeader *)((char *)pHeader + sizeof(PEHeader));
  763.  
  764.             if (pOpt->dictentries < 1)
  765.                 return NULL;
  766.  
  767.             pExportDir = (PEExportDirectory *)(pBase + pOpt->export_RVA);
  768.         }
  769.         break;
  770.     case 0x20b:        // PE32+
  771.         {
  772.             PE32PlusOptionalHeader *pOpt = (PE32PlusOptionalHeader *)((char *)pHeader + sizeof(PEHeader));
  773.  
  774.             if (pOpt->dictentries < 1)
  775.                 return NULL;
  776.  
  777.             pExportDir = (PEExportDirectory *)(pBase + pOpt->export_RVA);
  778.         }
  779.         break;
  780.  
  781.     default:
  782.         return NULL;
  783.     }
  784.  
  785.     // Hmmm... no exports?
  786.  
  787.     if ((char *)pExportDir == pBase)
  788.         return NULL;
  789.  
  790.     // Find the location of the export information.
  791.  
  792.     ulong *pNameTbl = (ulong *)(pBase + pExportDir->nametbl_ptr);
  793.     ulong *pAddrTbl = (ulong *)(pBase + pExportDir->addrtbl_ptr);
  794.     ushort *pOrdTbl = (ushort *)(pBase + pExportDir->ordtbl_ptr);
  795.  
  796.     // Scan exports.
  797.  
  798.     const char *pszName = NULL;
  799.     ulong bestdelta = 0xFFFFFFFF;
  800.     int i;
  801.  
  802.     addr -= (ulong)pBase;
  803.  
  804.     for(i=0; i<pExportDir->nametbl_cnt; i++) {
  805.         ulong fnaddr;
  806.         int idx;
  807.  
  808.         idx = pOrdTbl[i];
  809.         fnaddr = pAddrTbl[idx];
  810.  
  811.         if (addr >= fnaddr) {
  812.             ulong delta = addr - fnaddr;
  813.  
  814.             if (delta < bestdelta) {
  815.                 bestdelta = delta;
  816.                 fnbase = fnaddr;
  817.  
  818.                 if (pNameTbl[i])
  819.                     pszName = pBase + pNameTbl[i];
  820.                 else {
  821.                     static char buf[8];
  822.  
  823.                     wsprintf(buf, "ord%d", pOrdTbl[i]);
  824.                     pszName = buf;
  825.                 }
  826.  
  827.             }
  828.         }
  829.     }
  830.  
  831.     return pszName;
  832. }
  833.  
  834. ///////////////////////////////////////////////////////////////////////////
  835.  
  836. static bool IsExecutableProtection(DWORD dwProtect) {
  837.     MEMORY_BASIC_INFORMATION meminfo;
  838.  
  839.     // Windows NT/2000 allows Execute permissions, but Win9x seems to
  840.     // rip it off.  So we query the permissions on our own code block,
  841.     // and use it to determine if READONLY/READWRITE should be
  842.     // considered 'executable.'
  843.  
  844.     VirtualQuery(IsExecutableProtection, &meminfo, sizeof meminfo);
  845.  
  846.     switch((unsigned char)dwProtect) {
  847.     case PAGE_READONLY:                // *sigh* Win9x...
  848.     case PAGE_READWRITE:            // *sigh*
  849.         return meminfo.Protect==PAGE_READONLY || meminfo.Protect==PAGE_READWRITE;
  850.  
  851.     case PAGE_EXECUTE:
  852.     case PAGE_EXECUTE_READ:
  853.     case PAGE_EXECUTE_READWRITE:
  854.     case PAGE_EXECUTE_WRITECOPY:
  855.         return true;
  856.     }
  857.     return false;
  858. }
  859.  
  860. static const char *CrashGetModuleBaseName(HMODULE hmod, char *pszBaseName) {
  861.     char szPath1[MAX_PATH];
  862.     char szPath2[MAX_PATH];
  863.  
  864.     __try {
  865.         DWORD dw;
  866.         char *pszFile, *period = NULL;
  867.  
  868.         if (!GetModuleFileName(hmod, szPath1, sizeof szPath1))
  869.             return NULL;
  870.  
  871.         dw = GetFullPathName(szPath1, sizeof szPath2, szPath2, &pszFile);
  872.  
  873.         if (!dw || dw>sizeof szPath2)
  874.             return NULL;
  875.  
  876.         strcpy(pszBaseName, pszFile);
  877.  
  878.         pszFile = pszBaseName;
  879.  
  880.         while(*pszFile++)
  881.             if (pszFile[-1]=='.')
  882.                 period = pszFile-1;
  883.  
  884.         if (period)
  885.             *period = 0;
  886.     } __except(1) {
  887.         return NULL;
  888.     }
  889.  
  890.     return pszBaseName;
  891. }
  892.  
  893. static bool ReportCrashCallStack(HWND hwnd, HANDLE hFile, const EXCEPTION_POINTERS *const pExc, bool fExtra) {
  894.     const CONTEXT *const pContext = (const CONTEXT *)pExc->ContextRecord;
  895.     HANDLE hprMe = GetCurrentProcess();
  896.     char *lpAddr = (char *)pContext->Esp;
  897.     int limit = 100;
  898.     unsigned long data, first_rva;
  899.     const char *debug_data = NULL;
  900.     const char *fnname_heap, *classname_heap, *rva_heap;
  901.     const unsigned long (*seg_heap)[2];
  902.     int seg_cnt;
  903.  
  904.     // Attempt to read debug file.
  905.  
  906.     {
  907.         char szPath[MAX_PATH];
  908.  
  909.         SpliceProgramPath(szPath, sizeof szPath, "VirtualDub.dbg");
  910.  
  911.         HANDLE hFile2;
  912.         bool fSuccessful = false;
  913.         LONG lFileSize;
  914.         DWORD dwActual;
  915.  
  916.         do {
  917.             hFile2 = CreateFile(szPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  918.  
  919.             if (INVALID_HANDLE_VALUE == hFile2) {
  920.                 SpliceProgramPath(szPath, sizeof szPath, "VirtualD.dbg");
  921.                 hFile2 = CreateFile(szPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  922.             }
  923.  
  924.             if (INVALID_HANDLE_VALUE == hFile2)
  925.                 break;
  926.  
  927.             lFileSize = GetFileSize(hFile2, NULL);
  928.  
  929.             if (0xFFFFFFFF == lFileSize)
  930.                 break;
  931.  
  932.             debug_data = (const char *)VirtualAlloc(NULL, lFileSize, MEM_COMMIT, PAGE_READWRITE);
  933.             if (!debug_data)
  934.                 break;
  935.  
  936.             if (!ReadFile(hFile2, (void *)debug_data, lFileSize, &dwActual, NULL) || dwActual!=lFileSize)
  937.                 break;
  938.  
  939.             fSuccessful = true;
  940.         } while(0);
  941.  
  942.         if (hFile2 != INVALID_HANDLE_VALUE)
  943.             CloseHandle(hFile2);
  944.  
  945.         if (!fSuccessful) {
  946.             if (debug_data)
  947.                 VirtualFree((void *)debug_data, 0, MEM_RELEASE);
  948.  
  949.             Report(hwnd, hFile, "Could not open debug resource file.");
  950.             return false;
  951.         }
  952.  
  953.         rva_heap = debug_data + 20;
  954.         classname_heap = rva_heap + ((long *)debug_data)[1];
  955.         fnname_heap = classname_heap + ((long *)debug_data)[2];
  956.         seg_heap = (unsigned long (*)[2])(fnname_heap + ((long *)debug_data)[3]);
  957.         seg_cnt = ((long *)debug_data)[4];
  958.  
  959.         first_rva = *(long *)rva_heap;
  960.         rva_heap += 4;
  961.     }
  962.  
  963.     // Get some module names.
  964.  
  965.     void *pModuleMem;
  966.     ModuleInfo *pModules = CrashGetModules(pModuleMem);
  967.  
  968.     // Walk up the stack.  Hopefully it wasn't fscked.
  969.  
  970.     if (*(long *)debug_data != version_num) {
  971.         Report(hwnd, hFile, "Wrong VIRTUALDUB.DBG file (build %d)", *(long *)debug_data);
  972.     } else {
  973.         data = pContext->Eip;
  974.         do {
  975.             int i;
  976.  
  977.             bool fValid = true;
  978.             char buf[7];
  979.             int len;
  980.             MEMORY_BASIC_INFORMATION meminfo;
  981.             long parm1=0, parm2=0;
  982.  
  983.             VirtualQuery((void *)data, &meminfo, sizeof meminfo);
  984.             
  985.             if (!IsExecutableProtection(meminfo.Protect) || meminfo.State!=MEM_COMMIT) {
  986. //                Report(hwnd, hFile, "Rejected: %08lx (%08lx)", data, meminfo.Protect);
  987.                 fValid = false;
  988.             }
  989.  
  990.             if (data != pContext->Eip) {
  991.                 len = 7;
  992.  
  993.                 while(len > 0 && !ReadProcessMemory(GetCurrentProcess(), (void *)(data-len), buf+7-len, len, NULL))
  994.                     --len;
  995.  
  996.                 fValid &= IsValidCall(buf+7, len);
  997.  
  998.                 if (fValid) {
  999.                     ReadProcessMemory(GetCurrentProcess(), (void *)(lpAddr+0), &parm1, 4, NULL);
  1000.                     ReadProcessMemory(GetCurrentProcess(), (void *)(lpAddr+4), &parm2, 4, NULL);
  1001.                 }
  1002.             }
  1003.             
  1004.  
  1005.             if (fValid) {
  1006.                 for(i=0; i<seg_cnt; i++)
  1007.                     if (data >= seg_heap[i][0] && data < seg_heap[i][0] + seg_heap[i][1])
  1008.                         break;
  1009.  
  1010.                 if (i>=seg_cnt) {
  1011.                     ModuleInfo *pMods = pModules;
  1012.                     ModuleInfo mi;
  1013.                     char szName[MAX_PATH];
  1014.  
  1015.                     mi.name = NULL;
  1016.  
  1017.                     if (pMods) {
  1018.                         while(pMods->name) {
  1019.                             if (data >= pMods->base && (data - pMods->base) < pMods->size)
  1020.                                 break;
  1021.  
  1022.                             ++pMods;
  1023.                         }
  1024.  
  1025.                         mi = *pMods;
  1026.                     } else {
  1027.  
  1028.                         // Well, something failed, or we didn't have either PSAPI.DLL or ToolHelp
  1029.                         // to play with.  So we'll use a nastier method instead.
  1030.  
  1031.                         mi.base = (unsigned long)meminfo.AllocationBase;
  1032.                         mi.name = CrashGetModuleBaseName((HMODULE)mi.base, szName);
  1033.                     }
  1034.  
  1035.                     if (mi.name) {
  1036.                         unsigned long fnbase;
  1037.                         const char *pExportName = CrashLookupExport((HMODULE)mi.base, data, fnbase);
  1038.  
  1039.                         if (pExportName)
  1040.                             Report(hwnd, hFile, "%08lx: %s!%s(%lx, %lx) [%08lx+%lx+%lx]", data, mi.name, pExportName, parm1, parm2, mi.base, fnbase, (data-mi.base-fnbase));
  1041.                         else
  1042.                             Report(hwnd, hFile, "%08lx: %s!%08lx(%lx, %lx)", data, mi.name, data - mi.base, parm1, parm2);
  1043.                     } else
  1044.                         Report(hwnd, hFile, "%08lx: %08lx(%lx, %lx)", data, data, parm1, parm2);
  1045.  
  1046.                     --limit;
  1047.                 } else {
  1048.                     int idx = -1;
  1049.                     const char *pp = rva_heap;
  1050.                     long rva = data;
  1051.                     long diff = 0;
  1052.  
  1053.                     // scan down the RVAs
  1054.  
  1055.                     rva -= first_rva;
  1056.  
  1057.                     while(rva >= 0 && pp<classname_heap) {
  1058.                         char c;
  1059.  
  1060.                         diff = 0;
  1061.                         do {
  1062.                             c = *pp++;
  1063.  
  1064.                             diff = (diff<<7) | (c & 0x7f);
  1065.                         } while(c & 0x80);
  1066.  
  1067.                         rva -= diff;
  1068.                         ++idx;
  1069.                     }
  1070.  
  1071.                     if (pp<classname_heap && idx>=0) {
  1072.                         const char *fn_name = GetNameFromHeap(fnname_heap, idx);
  1073.                         const char *class_name = NULL;
  1074.                         const char *prefix = "";
  1075.  
  1076.                         if (*fn_name < 32) {
  1077.                             int class_idx;
  1078.  
  1079.                             class_idx = ((unsigned)fn_name[0] - 1)*128 + ((unsigned)fn_name[1] - 1);
  1080.                             class_name = GetNameFromHeap(classname_heap, class_idx);
  1081.  
  1082.                             fn_name += 2;
  1083.  
  1084.                             if (*fn_name == 1) {
  1085.                                 fn_name = class_name;
  1086.                             } else if (*fn_name == 2) {
  1087.                                 fn_name = class_name;
  1088.                                 prefix = "~";
  1089.                             } else if (*fn_name < 32)
  1090.                                 fn_name = "(special)";
  1091.                         }
  1092.  
  1093.                         Report(hwnd, hFile, "%08lx: %s%s%s%s(%lx, %lx)", data, class_name?class_name:"", class_name?"::":"", prefix, fn_name, parm1, parm2);
  1094.                         --limit;
  1095.                     } else fValid = false;
  1096.                 }
  1097.             }
  1098.  
  1099.             if (fValid && fExtra) {
  1100.                 char c;
  1101.                 char buf[80];
  1102.                 char *dst = (char *)parm1;
  1103.                 int j;
  1104.  
  1105.                 // xxxxxxxx: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx ................
  1106.  
  1107.                 for(j=0; j<2; j++) {
  1108.                     wsprintf(buf, "%08lx:%65c", dst, ' ');
  1109.  
  1110.                     for(i=0; i<16; i++)
  1111.                         if (ReadProcessMemory(GetCurrentProcess(), dst+i, &c, 1, NULL)) {
  1112.                             wsprintf(buf+10+3*i, "%02x", (int)(unsigned char)c);
  1113.                             buf[12+3*i]=' ';
  1114.                             buf[58+i]=isprint(c)?c:'.';
  1115.                         } else {
  1116.                             buf[10+3*i]='?';
  1117.                             buf[11+3*i]='?';
  1118.                             buf[58+i]='?';
  1119.                         }
  1120.  
  1121.                     Report(hwnd, hFile, "\t%s", buf);
  1122.  
  1123.                     dst = (char *)parm2;
  1124.                 }
  1125.                 Report(hwnd, hFile, "");
  1126.  
  1127.             }
  1128.  
  1129.             lpAddr += 4;
  1130.         } while(limit > 0 && ReadProcessMemory(hprMe, lpAddr-4, &data, 4, NULL));
  1131.     }
  1132.  
  1133.     if (pModuleMem)
  1134.         VirtualFree(pModuleMem, 0, MEM_RELEASE);
  1135.  
  1136.     VirtualFree((void *)debug_data, 0, MEM_RELEASE);
  1137.  
  1138.     return true;
  1139. }
  1140.  
  1141. void DoSave(const EXCEPTION_POINTERS *pExc, bool fSaveExtra) {
  1142.     HANDLE hFile;
  1143.     char szModName2[MAX_PATH];
  1144.     char tbuf[256];
  1145.     long idx;
  1146.  
  1147.     SpliceProgramPath(szModName2, sizeof szModName2, "crashinfo.txt");
  1148.  
  1149.     hFile = CreateFile(szModName2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  1150.  
  1151.     if (INVALID_HANDLE_VALUE == hFile)
  1152.         return;
  1153.  
  1154.     Report(NULL, hFile,
  1155.             "VirtualDub crash report -- build %d\r\n"
  1156.             "--------------------------------------\r\n"
  1157.             "\r\n"
  1158.             "Disassembly:", version_num);
  1159.  
  1160.     idx = 0;
  1161.  
  1162.     while(idx = g_pcdw->getInstruction(tbuf, idx)) {
  1163.         Report(NULL, hFile, "%s", tbuf);
  1164.     }
  1165.  
  1166.     Report(NULL, hFile, "");
  1167.  
  1168.     // Detect operating system.
  1169.  
  1170.     OSVERSIONINFO ovi;
  1171.     ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  1172.  
  1173.     if (GetVersionEx(&ovi)) {
  1174.         Report(NULL, hFile, "Windows %d.%d (Win%s build %d) [%s]"
  1175.             ,ovi.dwMajorVersion
  1176.             ,ovi.dwMinorVersion
  1177.             ,ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
  1178.             ? (ovi.dwMinorVersion>0 ? "98" : "95")
  1179.                 : ovi.dwPlatformId == VER_PLATFORM_WIN32_NT
  1180.                     ? (ovi.dwMajorVersion >= 5 ? "2000" : "NT")
  1181.                     : "?"
  1182.             ,ovi.dwBuildNumber & 0xffff
  1183.             ,ovi.szCSDVersion);
  1184.     }
  1185.  
  1186.     Report(NULL, hFile, "");
  1187.  
  1188.     ReportCrashData(NULL, NULL, hFile, pExc);
  1189.  
  1190.     Report(NULL, hFile, "");
  1191.  
  1192.     ReportCrashCallStack(NULL, hFile, pExc, fSaveExtra);
  1193.  
  1194.     Report(NULL, hFile, "\r\n-- End of report");
  1195.  
  1196.     CloseHandle(hFile);
  1197. }
  1198.  
  1199. void DoHelp(HWND hwnd) {
  1200.     char buf[512];
  1201.  
  1202.     strcpy(buf, HelpGetPath());
  1203.     strcat(buf, ">Helpme");
  1204.  
  1205.     WinHelp(hwnd, buf, HELP_CONTEXT, IDH_CRASH);
  1206. }
  1207.  
  1208. BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  1209.     static const EXCEPTION_POINTERS *s_pExc;
  1210.     static bool s_bHaveCallstack;
  1211.  
  1212.     switch(msg) {
  1213.  
  1214.         case WM_INITDIALOG:
  1215.             {
  1216.                 HWND hwndList1 = GetDlgItem(hDlg, IDC_ASMBOX);
  1217.                 HWND hwndList2 = GetDlgItem(hDlg, IDC_REGDUMP);
  1218.                 HWND hwndList3 = GetDlgItem(hDlg, IDC_CALL_STACK);
  1219.                 HWND hwndReason = GetDlgItem(hDlg, IDC_STATIC_BOMBREASON);
  1220.                 const EXCEPTION_POINTERS *const pExc = (const EXCEPTION_POINTERS *)lParam;
  1221.                 const EXCEPTION_RECORD *const pRecord = (const EXCEPTION_RECORD *)pExc->ExceptionRecord;
  1222.                 const CONTEXT *const pContext = (const CONTEXT *)pExc->ContextRecord;
  1223.  
  1224.                 s_pExc = pExc;
  1225.  
  1226.                 g_pcdw->DoInitListbox(hwndList1);
  1227.  
  1228.                 SendMessage(hwndList2, WM_SETFONT, SendMessage(hwndList1, WM_GETFONT, 0, 0), MAKELPARAM(TRUE, 0));
  1229.                 SendMessage(hwndList3, WM_SETFONT, SendMessage(hwndList1, WM_GETFONT, 0, 0), MAKELPARAM(TRUE, 0));
  1230.  
  1231.                 ReportCrashData(hwndList2, hwndReason, NULL, pExc);
  1232.                 s_bHaveCallstack = ReportCrashCallStack(hwndList3, NULL, pExc, false);
  1233.  
  1234.             }
  1235.             return TRUE;
  1236.  
  1237.         case WM_COMMAND:
  1238.             switch(LOWORD(wParam)) {
  1239.             case IDCANCEL: case IDOK:
  1240.                 EndDialog(hDlg, FALSE);
  1241.                 return TRUE;
  1242.             case IDC_SAVEPLUS:
  1243.             case IDC_SAVE2:
  1244.                 if (!s_bHaveCallstack)
  1245.                     if (IDOK != MessageBox(hDlg,
  1246.                         "VirtualDub cannot load its crash resource file, and thus the crash dump will be "
  1247.                         "missing the most important part, the call stack. Crash dumps are much less useful "
  1248.                         "to the author without the call stack.",
  1249.                         "VirtualDub warning", MB_OK|MB_ICONEXCLAMATION))
  1250.                         return TRUE;
  1251.  
  1252.                 DoSave(s_pExc, LOWORD(wParam)==IDC_SAVEPLUS);
  1253.                 return TRUE;
  1254.             case IDC_HELP2:
  1255.                 DoHelp(hDlg);
  1256.                 return TRUE;
  1257.             }
  1258.             break;
  1259.  
  1260.         case WM_MEASUREITEM:
  1261.             return g_pcdw->DoMeasureItem(lParam);
  1262.  
  1263.         case WM_DRAWITEM:
  1264.             return g_pcdw->DoDrawItem(lParam);
  1265.     }
  1266.  
  1267.     return FALSE;
  1268. }
  1269.